package sf.database.dialect.db;

import sf.core.DBField;
import sf.database.dbinfo.Feature;
import sf.database.dialect.DBDialect;
import sf.database.dialect.DBProperty;
import sf.database.dialect.Keywords;
import sf.database.meta.ColumnMapping;
import sf.database.meta.TableMapping;
import sf.database.support.DBMS;
import sf.querydsl.QueryDSLSupportDatabase;
import sf.tools.JavaTypeUtils;
import sf.tools.StringUtils;
import sf.tools.SystemUtils;

import java.sql.Connection;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * DB2,注意该dialect没有测试
 */
public class DB2Dialect extends DBDialect {

    public DB2Dialect() {
        this.keywords.addAll(Keywords.DB2);
        features.add(Feature.SUPPORT_IDENTITY);
        features.add(Feature.SUPPORT_COMMENT);
        setProperty(DBProperty.ADD_COLUMN, "ADD COLUMN");
        setProperty(DBProperty.MODIFY_COLUMN, "MODIFY COLUMN");
        setProperty(DBProperty.DROP_COLUMN, "DROP COLUMN");
        setProperty(DBProperty.WRAP_FOR_KEYWORD, "\"\"");
        setProperty(DBProperty.NVL_FUNCTION, "coalesce(%1$s,%2$s)");
    }

    @Override
    public String getName() {
        return DBMS.db2.name();
    }

    @Override
    public int getNumber() {
        return DBMS.db2.getNumber();
    }

    @Override
    public String getHibernateDialect() {
        return "org.hibernate.dialect.DB2Dialect";
    }

    @Override
    public String sqlTableDrop(String table) {
        return String.format("drop table if exists %s;", wrapKeyword(table));
    }

    @Override
    public String sqlTableRename(String oldName, String newName) {
        return String.format("rename table  %s to %s;", wrapKeyword(oldName), wrapKeyword(newName));
    }

    @Override
    public String sqlColumnRename(String table, String oldColumnName, String newColumnName) {
        return null;
    }

    @Override
    public String sqlColumnAdd(String table, String column_definition, String column_position) {
        String sql = String.format("alter table %s add %s", wrapKeyword(table), column_definition);
        if (supportsColumnPosition() && column_position != null) {
            sql = sql + " " + column_position;
        }
        return sql + ";";
    }

    @Override
    public String sqlColumnModify(String table, String column_definition, String column_position) {
        String sql = String.format("alter table %s modify %s", wrapKeyword(table), column_definition);
        if (supportsColumnPosition() && column_position != null) {
            sql = sql + " " + column_position;
        }
        return sql + ";";
    }

    @Override
    public String sqlColumnDrop(String table, String column) {
        return String.format("alter table %s drop %s;", wrapKeyword(table), wrapKeyword(column));
    }

    @Override
    public boolean createEntity(Connection conn, TableMapping en) {
        StringBuilder sb = new StringBuilder("CREATE TABLE " + wrapKeyword(en.getTableName()) + "(");
        // 创建字段
        boolean delimiter = false;
        for (Map.Entry<DBField, ColumnMapping> entry : en.getSchemaMap().entrySet()) {
            ColumnMapping cm = entry.getValue();
            if (cm.getJpaTransient() != null) {
                continue;
            }
            sb.append(delimiter ? "," : "").append(SystemUtils.lineSeparator);
            sb.append(wrapKeyword(cm.getRawColumnName()));
            // 自增主键特殊形式关键字
            if (cm.isPk() && cm.getGv() != null && JavaTypeUtils.isNumberClass(cm.getClz())) {
                sb.append(" generated by default as identity ");
            } else {
                // 普通字段
                sb.append(' ').append(evalFieldDefinition(cm));
            }
            if (cm.isPk() && en.getPkFields().size() == 1) {
                sb.append(" primary key ");
            }
            delimiter = true;
        }
        // 结束表字段设置
        sb.append(')');

        //指定表空间
        if (en.getTable() != null && StringUtils.isNotBlank(en.getTable().schema())) {
            sb.append(String.format(" IN %s", en.getTable().schema()));
        }

        List<String> sqls = new ArrayList<String>();
        sqls.add(sb.toString());

        // 创建复合主键
        List<ColumnMapping> pks = en.getPkFields();
        if (pks.size() > 1) {
            StringBuilder pkNames = new StringBuilder();
            for (ColumnMapping pk : pks) {
                pkNames.append(wrapKeyword(pk.getRawColumnName())).append(',');
            }
            pkNames.setLength(pkNames.length() - 1);

            String pkNames2 = en.getTableName();

            String sql = String.format("alter table %s add constraint pk_%s primary key (%s)",
                    wrapKeyword(en.getTableName()),
                    pkNames2,
                    pkNames);
            sqls.add(sql);
        }

        // 创建索引
        sqls.addAll(createIndexSql(en));
        //创建约束
        sqls.addAll(createUniqueSql(en));
        // 添加注释(表注释与字段注释)
        sqls.addAll(addCommentSql(en));
        // 执行创建语句
        execute(sqls, conn);

        // 创建关联表
        createRelation(conn, en);

        return true;
    }

    @Override
    public String evalFieldType(ColumnMapping mf) {
        int length = getColumnLength(mf);
        int scale = getColumnScale(mf);
        int precision = getColumnPrecision(mf);
        switch (mf.getSqlType()) {
            case Types.BOOLEAN:
                return "SMALLINT";
            case Types.INTEGER:
                // 用户自定义了宽度
                // if (mf.getWidth() > 0)
                // return "decimal(" + mf.getWidth() + ")";
                // 用数据库的默认宽度
                return "INTEGER";

            case Types.FLOAT:
                // 用户自定义了精度
                if (scale > 0 && precision > 0) {
                    return "decimal(" + precision + "," + scale + ")";
                }
                return "FLOAT";
            case Types.REAL:
            case Types.DOUBLE:
                // 用默认精度
                return "decimal(15,10)";
            case Types.TIMESTAMP:
                return "TIMESTAMP";
            default:
                break;
        }
        return super.evalFieldType(mf);
    }

    @Override
    public String uniqueSql(String tableName, String name, String[] columnNames) {
        StringBuilder sb = new StringBuilder();
        sb.append("alter table ").append(wrapKeyword(tableName)).append(" add constraint ").append(name).append(" unique(");
        for (int i = 0; i < columnNames.length; i++) {
            String column = columnNames[i];
            sb.append(i > 0 ? "," : "").append(wrapKeyword(column));
        }
        sb.append(")");
        return sb.toString();
    }

    @Override
    public StringBuilder sqlPageList(StringBuilder sql, long offset, int limit) {
        long pageEnd = offset + limit - 1;
        sql.insert(0, " SELECT * FROM ( SELECT inner_query_b.*, ROWNUMBER() OVER() rn  FROM (")
                .append("	) AS inner_query_b )AS inner_query_a WHERE inner_query_a.rn BETWEEN ")
                .append(offset).append(" and ").append(pageEnd);
        return sql;
    }

    @Override
    public String getQueryDslDialect() {
        return QueryDSLSupportDatabase.DB2;
    }

    @Override
    public String getJooqDialect() {
        return null;
    }
}
